PA:Physical Address 物理地址,
PPN:Physical Page Number 物理页号,
PPO:Physical Page Offset 物理页面偏移,
VA:Virtual Address 虚拟地址,
VPN:Virtual Page Number 虚拟页号,
VPO:Virtual Page Offset 虚拟页面偏移,
MMU:Memory Management Unit 内存管理单元
地址空间:一个非负整数地址的有序集合
线性地址空间:
VAS:虚拟地址空间
PAS :物理地址空间
VP:Virtual Page 虚拟页 ,大小
PP: Physical Page 物理页/页帧,大小
PTE:Page Table Entry 页表条目,页表将虚拟页映射到物理页
PTBR:Page Table Base Register 页表基址寄存器
PTEA:页表条目地址
TLB:Translation Lookaside Buffer 快表,针对PTE的缓存,存储在MMU中
TLBT:TLB索引,
TLBI:TLB标记,
段页式管理:分页与分段的区别
页是信息的物理信息,分页的主要目的是为了实现离散分配,提高内存利用率,分页仅仅是系统管理的需求,对用户不可见。页的大小固定且由系统决定。
段是信息的逻辑单位,分段的主要目的是为了更好地满足用户需求,分段对用户可见。段的长度不固定决定于用户编写的程序。
分段比分页更容易实现信息的共享和保护。因为分页时一个进程不能完全装入一个页中,因此不便操作。
MMU将代码和数据的虚拟地址映射成实际系统中的物理地址。
这种转换是在硬件中自动进行的,对应用程序是透明的。
除了地址转换外,MMU还控制内存访问权限、内存排序和每个区域内存的缓存策略。
期望:足够快的,足够大的,私有的
它将主存看成是一个存储在磁盘上的地址空间的高速缓存,在主存中只保存活动区域,并根据需要在磁盘和主存之间来回传送数据,通过这种方式,它高效地使用了主存。
它为每个进程提供了一致的地址空间,从而简化了内存管理。
它保护了每个进程的地址空间不被其他进程破坏。
Memory hierarchy
DRAM硬件组织结构
内存是磁盘的缓存
在VM的惯用说法中,页——缓存块,交换/页面调度——传送块
物理页在内存中,虚拟页在磁盘中
采用按需页面调度的方式
DRAM缓存是全相联的,任意物理页都可以包含任意虚拟页
在任意时刻,虚拟页面的集合都分为三个不相交的子集:
未分配的:VM 系统还未分配(或者创建)的页。
未分配的块没有任何数据和它们相关联,因此也就不占用任何磁盘空间。
缓存的:当前已缓存在物理内存中的已分配页。
未缓存的:未缓存在物理内存中的已分配页
抽象地,假定PTE由一个有效位和一个n位地址字段组成的
具体地,x86的页表项组成
x86-32
x86-64
Waiting until the miss to copy the page to DRAM is known as demand paging.(按需页面调度)
操作系统位每个进程提供一个独立的页表,也即一个独立的虚拟地址空间
简化链接:内存映像基本格式的一致性,允许链接器生成独立于物理内存的完全链接的可执行文件
简化加载:加载器只分配虚拟页,只有初次被引用时才将数据复制到内存中
简化共享:将不同进程中适当的虚拟页面映射到相同的物理页面,从而安排多个进程共享这部分代码的一个副本
简化内存分配:由于页表的工作方式,要求额外分配内存时,分配连续k个虚拟页,并映射到任意k个物理页
直接利用PTE实现内存保护
四个许可位
SUP:是否必须运行内核模式才能访问该页
READ:可读
WRITE:可写
EXEC:可执行
一般保护故障:Linux报告为 Segmentation fault
考虑一个有SRAM高速缓存和VM的系统
物理地址直接访问Disk
物理地址访问+MM作为Disk的缓存
物理地址访问+Cache作为MM的缓存+MM作为Disk的缓存
虚拟地址访问+地址翻译+Cache作为MM的缓存+MM作为Disk的缓存
基本步骤
PTE hit:存在物理页缓存了该虚拟页,即MM中有Disk的缓存
PTE miss:不存在物理页缓存了该虚拟页,触发page fault更新MM中的物理页(该过程对地址翻译隐藏)
基本步骤不变,Cache实现所有数据的缓存(该过程对地址翻译隐藏)
细节注意:
Cache分别存储PTE和Data(Data的高速缓存使用物理寻址),因而不会破坏地址翻译的基本流程
否则,若Data的高速缓存直接使用虚拟寻址,跳过地址翻译,则需要高速缓存额外处理访问权限等保护问题
基本步骤
TLB hit:TLB中缓存了该VPN对应的PTE,所有地址翻译都在MMU中快速实现
TLB miss:MMU访问L1 Cache取出相应的PTE
基本步骤
多级页表
TWU:Table Walking Unit
例子:64位系统,
设计
虚拟地址中用以访问TLB的组成部分
TLB结构:8路组相连的命中率几乎和全相连命中率几乎一样
TLB存储:Contains complete page table entries for small number of pages
小页面的优点
内部碎片:随便选择一个数据段、堆栈段很可能不会恰好装满整数个页面,平均有一半为空
运行小型程序时,对于内存的占用较少
小页面的缺点
小页面,意味着需要更多的页面和更大的页表:存储空间+传输时间
对TLB不友好:小页表意味着地址转换时需要更多的页表映射表项,意味着TLB缓存的表项多,降低TLB的命中率;
只考虑碎片浪费和页表所需的内存
48 位虚拟地址空间,52 位物理地址空间
地址翻译过程中可用的地址空间是 48 位(实际是 47,因为任何虚拟地址的 48 位至 63 位必须与第 47 位相同)
所有 TLB 均为 4 路组相联,以虚拟地址访问
L1 d-TLB、L1 i-TLB、L2 unified TLB 分别有 64、128、512 个页表项
单核内所有 Cache 均为 8 路组相联,物理地址访问
L1 d-cache、L1 i-cache、L2 unidifed cache 大小分别为 32KB、32KB、256KB
多核共享 L3 Cache,16 路组相联,大小为 8MB
Core i7 要求页大小为 4KB 或 4MB
Linux 页表大小总为 4 KB(12 位)
P:子页表在/不在物理内存
R/W: 所有可访问页的只读或读写权限
U/S: 所有可访问页的用户或超级用户(内核)访问权限
WT:子页表直写/写回缓存策略
CD:能/不能缓存子页表
A: 引用位(每次访问都会设置,可以用它实现页替换算法)
PS:4KB 或 4MB 页大小(只对第一层PTE定义)
Base addr:子页表物理地址的高 40 位(物理页表 4KB 对齐)
XD:能/不能从这个 PTE 可访问的所有页中取指令
P:子页在/不在物理内存
R/W: 子页的只读或读写权限
U/S: 子页的用户或超级用户(内核)访问权限
WT:子页直写/写回缓存策略
CD:能/不能缓存子页
A: 引用位(每次访问都会设置,可以用它实现页替换算法)
D: 修改位(写后设置,决定是否写回牺牲页)
G: 全局位(任务切换时,不从 TLB 中驱逐出去)
Base addr:子页物理地址的高 40 位(物理页 4KB 对齐)
XD:能/不能从这个子页中取指令


每个进程共享内核代码和全局数据
页表和内核在进程的上下文运行代码时使用的栈等每个进程不同
Linux 将物理内存完整地映射到内核虚拟内存的一段连续页,方便访问物理内存

每个进程有一个 task_struct
包含 PID、指向用户栈的指针、可执行目标文件的名字、以及程序计数器等
pgd: 指向第一级页表(页全局目录)的基址
mmap: 指向一个 vm_area_structs(区域结构)的链表
vm_start:指向这个区域的起始处
vm_end:指向这个区域的结束处
vm_prot:描述这个区域内包含的所有页的读写许可权限
vm_flags:描述这个区域内的页面是与其他进程共享的,还是这个进程私有的(还描述了其他一些信息)
vm_next:指向链表中下一个区域结构。

地址是否存在:是否在某个段内
访问是否合法:查看读/写/执行位
否则,缺页导致:将页面调入


fork和execve

mmap和munmap

DMA:动态内存分配器,对虚拟内存进行管理
分配
xxxxxxxxxx11void *malloc(size_t size); //成功:返回已分配块的指针;错误:NULL(双字)对齐原则
32位:地址总是8的倍数
64位:地址总是16的倍数
堆假设块是8字节双字边界对齐的
不初始化所返回的内存
calloc:Version of malloc that initializes allocated block to zero
xxxxxxxxxx11void *calloc(size_t n, size_t size);realloc:Changes the size of a previously allocated block
xxxxxxxxxx11void realloc(void *tr, size_t new_size);sbrk:Used internally by allocators to grow or shrink the heap
xxxxxxxxxx11void *sbrk(intptr_t incr); //Returns: old brk pointer on success, −1 on error回收
xxxxxxxxxx11void free(void *ptr); //Returns: nothing
处理任意请求序列
立即响应请求
只使用堆
对齐块(对齐要求)
不修改已分配的块
最大化呑吐率
最大化内存利用率

内部碎片:已分配块比有效载荷大
外部碎片:空闲内存合计可满足分配请求,但单独空闲块不够
空闲块组织:我们如何记录空闲块?
放置:我们如何选择一个合适的空闲块来放置一个新分配的块?
分割:在将一个新分配的块放置到某个空闲块之后,我们如何处理这个空闲块中的剩余部分?
合并:我们如何处理一个刚刚被释放的块?

堆块格式

xxxxxxxxxx51typedef struct block2{3 word_t header;4 unsigned char payload[0]; //Zero length array5} block_t;
组织形式

分割空闲块

获取额外的堆内存

合并的概念
当分配器释放一个已分配块时,可能有其他空闲块与这个新释放的空闲块相邻。这些邻接的空闲块可能引起一种现象,叫做假碎片(fault fragmentation)
任何实际的分配器都必须合并相邻的空闲块,这个过程称为合并(coalescing)
合并策略
立即合并(immediate coalescing)
推迟合并(deferred coalescing)
边界标记实现

考虑当分配器释放当前块时所有可能存在的情况:


边界标记优化:只有空闲块需要尾部


块结构

组织方式:双向空闲链表
排序策略


